<template>
	<view class="e-count-down">
		<slot>
			<text class="e-count-down__text">{{ formattedTime }}</text>
		</slot>
	</view>
</template>

<script lang="ts">
	import {
		defineComponent
	} from 'vue'
	import mpMixin from '../../libs/mixin/mpMixin'
	/**
	 * e-count-down 倒计时
	 * @description 该组件一般使用于某个活动的截止时间上，通过数字的变化，给用户明确的时间感受，提示用户进行某一个行为操作。
	 * @tutorial https://uviewui.com/components/countDown.html
	 * @property {String | Number}	time		倒计时时长，单位ms （默认 0 ）
	 * @property {String}			format		时间格式，DD-日，HH-时，mm-分，ss-秒，SSS-毫秒  （默认 'HH:mm:ss' ）
	 * @property {Boolean}			autoStart	是否自动开始倒计时 （默认 true ）
	 * @property {Boolean}			millisecond	是否展示毫秒倒计时 （默认 false ）
	 * @event {Function} finish 倒计时结束时触发 
	 * @event {Function} change 倒计时变化时触发 
	 * @event {Function} start	开始倒计时
	 * @event {Function} pause	暂停倒计时 
	 * @event {Function} reset	重设倒计时，若 auto-start 为 true，重设后会自动开始倒计时 
	 * @example <e-count-down :time="time"></e-count-down>
	 */
	export default defineComponent({
		name: "e-count-down",
		mixins: [mpMixin]
	});
</script>

<script lang="ts" setup>
	import {
		watch,
		ref,
		toRefs,
		onBeforeUnmount
	} from 'vue'
	import {
		countDownProps,
		countDownEmit
	} from './props'
	import {
		useDefaultProps
	} from '../../libs/hooks'
	import componentsProps, {
		type DefaultProps
	} from "../../libs/config/componentsProps";

	import {
		isSameSecond,
		parseFormat,
		parseTimeData
	} from './utils';

	const {
		countDown: countDownDefaultProps
	} = toRefs(componentsProps);


	const _props = defineProps(countDownProps)
	const props = useDefaultProps<DefaultProps['countDown']>(_props, countDownDefaultProps);
	const emit = defineEmits(countDownEmit)

	let timer:undefined | number = undefined
	// 倒计时是否正在进行中
	let runing = false
	let endTime = 0 // 结束的毫秒时间戳
	let remainTime = 0 // 剩余的毫秒时间
	// 格式化后的时间，如"03:23:21"
	const formattedTime = ref('0')



	// 开始倒计时
	const start = () => {
		if (runing) return
		// 标识为进行中
		runing = true
		// 结束时间戳 = 此刻时间戳 + 剩余的时间
		endTime = Date.now() + remainTime
		toTick()
	}
	// 根据是否展示毫秒，执行不同操作函数
	const toTick = () => {
		if (props.value.millisecond) {
			microTick()
		} else {
			macroTick()
		}
	}
	const macroTick = () => {
		_clearTimeout()
		// 每隔一定时间，更新一遍定时器的值
		// 同时此定时器的作用也能带来毫秒级的更新
		timer = setTimeout(() => {
			// 获取剩余时间
			const remain = getRemainTime()
			// 重设剩余时间
			if (!isSameSecond(remain, remainTime) || remain === 0) {
				setRemainTime(remain)
			}
			// 如果剩余时间不为0，则继续检查更新倒计时
			if (remainTime !== 0) {
				macroTick()
			}
		}, 30)
	}
	const microTick = () => {
		_clearTimeout()
		timer = setTimeout(() => {
			setRemainTime(getRemainTime())
			if (remainTime !== 0) {
				microTick()
			}
		}, 50)
	}
	// 获取剩余的时间
	const getRemainTime = () => {
		// 取最大值，防止出现小于0的剩余时间值
		return Math.max(endTime - Date.now(), 0)
	}
	// 设置剩余的时间
	const setRemainTime = (remain : number) => {
		remainTime = remain
		// 根据剩余的毫秒时间，得出该有天，小时，分钟等的值，返回一个对象
		const timeData = parseTimeData(remain)
		emit('change', timeData)
		// 得出格式化后的时间
		formattedTime.value = parseFormat(props.value.format, timeData)
		// 如果时间已到，停止倒计时
		if (remain <= 0) {
			pause()
			emit('finish')
		}
	}
	// 重置倒计时
	const reset = () => {
		pause()
		remainTime = Number(props.value.time)
		setRemainTime(remainTime)
		if (props.value.autoStart) {
			start()
		}
	}
	// 暂停倒计时
	const pause = () => {
		runing = false;
		_clearTimeout()
	}
	// 清空定时器
	const _clearTimeout = () => {
		clearTimeout(timer)
		timer = undefined
	}


	watch(() => props.value.time, () => {
		reset()
	})

	reset()

	onBeforeUnmount(() => {
		_clearTimeout()
	})
</script>

<style lang="scss" scoped>
	@import "../../libs/theme/count-down.scss";
</style>